from agent import agent
from packetholder import packetholder
from packet import packet
from constants import SYSTEM, PRIO, BINAGENT, MAGIC

class binary_agent(agent):
  in_holders = None
  out_holders = None

  output = None
  link_bw_f = None

  def __init__(self):
    super(binary_agent, self).__init__()
    
    self.in_holders = [None] * PRIO.NUM_LEVELS
    self.out_holders = [None] * PRIO.NUM_LEVELS
    for level in range(PRIO.NUM_LEVELS):
      self.in_holders[level] = packetholder(BINAGENT.MAX_PACKETS)
      self.out_holders[level] = packetholder(BINAGENT.MAX_PACKETS)

  def accept(self, src_node, inc_pkt, max_trans_f, route_hint=None):
    return self.in_holders[inc_pkt.priority].accept(inc_pkt, max_trans_f)

  def link_out(self, agent_out, new_link_bw_f):
    self.output = agent_out
    self.link_bw_f = new_link_bw_f

  def handle_packet(self, pkt):
    return True # return True to discard packet

  # returns packet to fill out if successful, otherwise returns None
  def issue_packet(self, pkt_size, pkt_priority):
    if(len(self.out_holders[pkt_priority].contents) < self.out_holders[pkt_priority].max_packets):
      new_pkt = packet(pkt_size)
      new_pkt.src = self.node_id
      new_pkt.priority = pkt_priority

      self.out_holders[pkt_priority].contents.append(new_pkt)
      return new_pkt
    else:
      return None

  def process(self):
    # Handle sending outputs
    #   age-based routing to decide who gets to leave first
    avail_f = self.link_bw_f
    for level in reversed(self.out_holders):
      level.contents = sorted(level.contents, key=packet.get_age)
      for pkt in level.contents:
        if(avail_f <= 0): break
        trans_f = self.transmit(self.output, pkt, avail_f)
        avail_f -= trans_f
   
    # Handle processing inputs
    for level in reversed(self.in_holders):
      remove_list = []
      for pkt in level.contents:
        if (pkt.valid()):
          if(self.handle_packet(pkt)):
            remove_list.append(pkt)

      for victim in remove_list:
        level.contents.remove(victim)
  
  def commit(self):
    for level in self.in_holders:
      level.commit()
    for level in self.out_holders:
      level.commit()
